package com.yvan.leto;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.yvan.Conv;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Set;

@Slf4j
public class AppConfig {
    private static final String SERVER_PROPERTIES_LINUX = "/opt/leto/settings.properties";
    private static final String SERVER_PROPERTIES_WINDOWS = "C:/opt/leto/settings.properties";
    private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader();

    private static final String LETO_ENV = "classpath:./leto-env.properties";
    private static final String LETO = "classpath:./leto.properties";

    private static final String DEFAULT_LETO_SERVER = "file:./repository";

    private static final String CFG_APP = "app";
    private static final String CFG_LETO_SERVER = "server";
    private static final String CFG_ENV = "env";
    private static final String CFG_CLIENT_ID = "clientid";

    /**
     * 配置中心地址
     */
    String letoServer;

    /**
     * 环境标识
     */
    String env;

    /**
     * 应用编号
     */
    String appId;

    /**
     * 客户端编号
     */
    String clientId;


    <T> AppConfig(Class<T> clazz) {
        final String envFile = IpUtils.isOSWindows() ? SERVER_PROPERTIES_WINDOWS : SERVER_PROPERTIES_LINUX;

        //载入配置文件
        Properties prop = new Properties();

        try {
            @Cleanup FileInputStream fi = new FileInputStream(envFile);
            prop.load(fi);
        } catch (IOException e) {
            log.error("load " + envFile, e);
        }
        prop = readConfigFile(LETO_ENV, prop);
        Properties env = System.getProperties();

        //定义全局配置中心地址
        String defaultLetoServer = env.getProperty(CFG_LETO_SERVER, prop.getProperty(CFG_LETO_SERVER, DEFAULT_LETO_SERVER));

        //获取当前环境标识
        this.env = env.getProperty(CFG_ENV, prop.getProperty(CFG_ENV, ""));
        if (Strings.isNullOrEmpty(this.env)) {
            //env 不允许为空
            throw new RuntimeException(CFG_ENV + " is null!");
        }

        //获取当前 clientId
        this.clientId = env.getProperty(CFG_CLIENT_ID, prop.getProperty(CFG_CLIENT_ID, ""));
        if (Strings.isNullOrEmpty(this.clientId)) {
            //clientid 不允许为空
            throw new RuntimeException(CFG_CLIENT_ID + " is null!");
        }

        //取当前配置中心地址
        final String envMetaServer = CFG_LETO_SERVER + "." + this.env;
        this.letoServer = env.getProperty(envMetaServer, prop.getProperty(envMetaServer, defaultLetoServer));
        if (Strings.isNullOrEmpty(this.env)) {
            //letoServer 不允许为空
            throw new RuntimeException(envMetaServer + " is null!");
        }

        // leto.properti 文件
        Properties propApp = new Properties();
        propApp = readConfigFile(LETO, propApp);
        //APP名称
        this.appId = Conv.NS(env.getProperty(CFG_APP, propApp.getProperty(CFG_APP, "")));
        if (Strings.isNullOrEmpty(appId)) {
            //AppId不允许为空
            throw new RuntimeException("appId is null! " + LETO + " is: [" +
                    Joiner.on(",").join(propApp.stringPropertyNames()) + "], " + LETO_ENV + " is :[" +
                    Joiner.on(",").join(prop.stringPropertyNames()) + "]"
            );
        }
    }

    public static Properties readConfigFile(String configPath, Properties defaults) {
        Properties props = new Properties();
        if (defaults != null) {
            props.putAll(defaults);
        }

        InputStream in = null;
        try {
            val resource = RESOURCE_LOADER.getResource(configPath);
            in = resource.getInputStream();

            if (in != null) {
                props.load(in);
            }
        } catch (IOException ex) {
            log.warn("Reading config failed: {}", ex.getMessage());

        } finally {
            try {
                in.close();
            } catch (IOException ex) {
            }
        }

        return props;
    }

    public void loadArgs(String... args) {
        if (args == null || args.length <= 0) {
            log.info("args is null!!");
            return;
        }

        for (String arg : args) {
            if (!arg.contains("=")) {
                continue;
            }
            String[] kvs = arg.split("\\=");
            if (kvs.length > 2) {
                throw new RuntimeException("error args:" + Joiner.on("=").join(kvs));
            }
            String k = kvs[0];
            String v = kvs[1];
            if (CFG_APP.equals(k)) {
                this.appId = v;

            } else if (CFG_LETO_SERVER.equals(k)) {
                this.letoServer = v;

            } else if (CFG_ENV.equals(k)) {
                Set<String> envs = Sets.newLinkedHashSet(Splitter.on(",").omitEmptyStrings().split(this.env));
                if (v.startsWith("+")) {
                    envs.addAll(Splitter.on(",").omitEmptyStrings().splitToList(v.substring(1)));

                } else if (v.startsWith("-")) {
                    envs.removeAll(Splitter.on(",").omitEmptyStrings().splitToList(v));

                } else {
                    envs = Sets.newLinkedHashSet(Splitter.on(",").omitEmptyStrings().split(v));

                }
                this.env = Joiner.on(",").join(envs);

           } else if (CFG_CLIENT_ID.equals(k)) {
                this.clientId = v;

            } else {
                throw new RuntimeException("error args:" + Joiner.on("=").join(kvs));
            }
        }

        log.info("args is {}", Joiner.on(",").join(args));
    }
}
